#ifndef __M_ROOM_H__
#define __M_ROOM_H__
#include "util.h"
#include "log.h"
#include "online.h"
#include "db.h"
#define BOARD_ROW 15
#define BOARD_COL 15
#define CHESS_WHITE 1
#define CHESS_BLACK 2
typedef enum { GAME_START, GAME_OVER }room_statu;
class room {
    private:
        uint64_t _room_id;
        room_statu _statu;
        int _player_count;
        uint64_t _white_id;
        uint64_t _black_id;
        users_tables *_tb_user;
        online_manager *_online_user;    
        std::vector<std::vector<int>> _board;
    private:
         bool five(int row, int col, int row_off, int col_off, int color) {
           int row_next=row+row_off;
           int col_next=col+col_off;
           int count=1;
           while(row_next>=0&&row_next<BOARD_ROW&&
                col_next>=0&&col_next<BOARD_COL&&
                color==_board[row_next][col_next])
                {
                    count++;
                    row_next+=row_off;
                    col_next+=col_off;

                }
                row_next=row-row_off;
                col_next=col-col_off;
             while(row_next>=0&&row_next<BOARD_ROW&&
                col_next>=0&&col_next<BOARD_COL&&
                color==_board[row_next][col_next])
                {
                    count++;
                    row_next-=row_off;
                    col_next-=col_off;

                }
                if(count>=5)
                {
                    return true;
                }
                else{
                    return false;
                }

        }
        int64_t check_win(int row, int col, int color) {
            bool ret=five(row,col,0,1,color)||five(row,col,1,0,color)||five(row,col,1,1,color)||five(row,col,1,-1,color);
            if(ret)
            {
                if(color==CHESS_WHITE)
                {
                    return _white_id;
                }
                else
                {
                    return _black_id;
                }
            }
            else
            {
                return 0;
            }


            
           
        }
    public:
        room(uint64_t room_id, users_tables *tb_user, online_manager *online_user):
            _room_id(room_id), _statu(GAME_START), _player_count(0),
            _tb_user(tb_user), _online_user(online_user),
            _board(BOARD_ROW, std::vector<int>(BOARD_COL, 0)){
            DBG_Log("%lu 房间创建成功!!", _room_id);
        }
        ~room() {
            DBG_Log("%lu 房间销毁成功!!", _room_id);
        }



        uint64_t id() { return _room_id; }
        room_statu statu() { return _statu; }
        int player_count() { return _player_count; }
        void add_white_user(uint64_t uid) { _white_id = uid; _player_count++; }
        void add_black_user(uint64_t uid) { _black_id = uid; _player_count++; }
        uint64_t get_white_user() { return _white_id; }
        uint64_t get_black_user() { return _black_id; }

        /*处理下棋动作*/
        Json::Value handle_chess(Json::Value &req) {
             Json::Value json_resp = req;
            //1.验证房间号是否匹配              
            // 2. 判断房间中两个玩家是否都在线，任意一个不在线，就是另一方胜利。
            int chess_row = req["row"].asInt();
            int chess_col = req["col"].asInt();
            uint64_t cur_uid = req["uid"].asUInt64();
            if(_online_user->is_in_game_room(_white_id)==false)
            {
                json_resp["result"] = true;
                json_resp["reason"] = "运气真好！对方掉线，不战而胜！";
                json_resp["winner"] = (Json::UInt64)_black_id;
                return json_resp;
            }
             if(_online_user->is_in_game_room(_black_id)==false)
            {
                json_resp["result"] = true;
                json_resp["reason"] = "运气真好！对方掉线，不战而胜！";
                json_resp["winner"] = (Json::UInt64)_white_id;
                return json_resp;
            }
           
            // 3. 获取走棋位置，判断当前走棋是否合理（位置是否已经被占用）
            if(_board[chess_row][chess_col]!=0)
            {
                json_resp["result"] = false;
                json_resp["reason"] = "当前位置已经有了其他棋子！";
                return json_resp;
            }
            int cur_color = cur_uid == _white_id ? CHESS_WHITE : CHESS_BLACK;
            _board[chess_row][chess_col] = cur_color;
            
            // 4. 判断是否有玩家胜利（从当前走棋位置开始判断是否存在五星连珠）
            int64_t winner_id=check_win(chess_row,chess_col,cur_color);
            if(winner_id!=0)
            {
                 json_resp["reason"] = "五星连珠，战无敌！";

            }
             json_resp["result"] = true;
            json_resp["winner"] = (Json::UInt64)winner_id;
            return json_resp;
        }
         /*处理聊天动作*/
        Json::Value handle_chat(Json::Value &req) {
            Json::Value json_resp=req;
            std::string s=req["message"].asString();
            size_t pos=s.find("垃圾");
            if(pos!=std::string::npos)
            {
                json_resp["result"] = false;
                json_resp["reason"] = "消息中包含敏感词，不能发送！";
                return json_resp;
            }
            else
            {
                json_resp["result"]=true;
               return json_resp;
            }


        }
        /*处理玩家退出房间动作*/
        void handle_exit(uint64_t uid) {
            Json::Value json_resp ;
            if(_statu==GAME_START)
            {

                uint64_t winner_id = (Json::UInt64)(uid == _white_id ? _black_id : _white_id);
                json_resp["optype"] = "put_chess";
                json_resp["result"] = true;
                json_resp["reason"] = "对方掉线，不战而胜！";
                json_resp["room_id"] = (Json::UInt64)_room_id;
                json_resp["uid"] = (Json::UInt64)uid;
                json_resp["row"] = -1;
                json_resp["col"] = -1;
                json_resp["winner"] = (Json::UInt64)winner_id;
                uint64_t loser_id = winner_id == _white_id ? _black_id : _white_id;
                _tb_user->win(winner_id);
                _tb_user->lose(loser_id);
                _statu = GAME_OVER;
                DBG_Log("Gameover");
                broadcast(json_resp);
            }
            _player_count--;
            DBG_Log("uid:%ld退出",uid);
            
            return ;
           
        }
        /*总的请求处理函数，在函数内部，区分请求类型，根据不同的请求调用不同的处理函数，得到响应进行广播*/
        void handle_request(Json::Value &req) {
            Json::Value json_resp=req;
            if(req["room_id"].asUInt64()!=_room_id)
            {
                ERR_Log("请求房间号:%ld和实际房间号:%ld不匹配",req["room_id"].asUInt64(),_room_id);
                json_resp["optype"] = req["optype"].asString();
                json_resp["result"] = false;
                json_resp["reason"] = "房间号不匹配！";
                return broadcast(json_resp);
                
            }
         std::string optype=req["optype"].asString();
         if(optype=="put_chess")
         {
             json_resp=handle_chess(req);
             if (json_resp["winner"].asUInt64() != 0) {
                    uint64_t winner_id = json_resp["winner"].asUInt64();
                    uint64_t loser_id = winner_id == _white_id ? _black_id : _white_id;
                    _tb_user->win(winner_id);
                    _tb_user->lose(loser_id);
                    _statu = GAME_OVER;
                }
         }
         else if(optype=="chat")
         {
            json_resp=handle_chat(req);
         }
         else
         {
            json_resp["optype"] = req["optype"].asString();
            json_resp["result"] = false;
            json_resp["reason"] = "未知请求类型";
         }
         broadcast(json_resp);
         return;

        }
        /*将指定的信息广播给房间中所有玩家*/
        void broadcast(Json::Value &rsp) {
            //1.序列化
            std::string response;
            Json_util::serialize(rsp,response);
            //2.获取连接
            int64_t user_id=rsp["uid"].asInt64();
            
             wsserver_t::connection_ptr white_connection=_online_user->get_conn_from_room(_white_id);
             wsserver_t::connection_ptr black_connection=_online_user->get_conn_from_room(_black_id);
             if(white_connection.get())
             {
                white_connection->send(response);
             }
             else {
                ERR_Log("房间-白棋玩家连接获取失败");
            }
           if(black_connection.get())
           {
                black_connection->send(response);
           }
           else {
                ERR_Log("房间-白棋玩家连接获取失败");
            }
            return ;
        }
};



using room_ptr = std::shared_ptr<room>;
class room_manager{
    private:
        uint64_t _next_rid;
        std::mutex _mutex;
        users_tables *_tb_user;
        online_manager *_online_user;
        std::unordered_map<uint64_t, room_ptr> _rooms;//房间id找房间
        std::unordered_map<uint64_t, uint64_t> _users;//用户id找房间id
    public:
        /*初始化房间ID计数器*/
        room_manager(users_tables *ut, online_manager *om):
            _next_rid(1), _tb_user(ut), _online_user(om) {
            DBG_Log("房间管理模块初始化完毕！");
        }
        ~room_manager() { DBG_Log("房间管理模块即将销毁！"); }
        //为两个用户创建房间，并返回房间的智能指针管理对象
        room_ptr create_room(uint64_t uid1, uint64_t uid2) {
            //两个用户在游戏大厅中进行对战匹配，匹配成功后创建房间
            //1. 校验两个用户是否都还在游戏大厅中，只有都在才需要创建房间。
            if(!_online_user->is_in_game_hall(uid1))
            {
                ERR_Log("id%ld用户不在大厅",uid1);
                return room_ptr();
            }
            if(!_online_user->is_in_game_hall(uid2))
            {
                ERR_Log("id%ld用户不在大厅",uid2);
                 return room_ptr();

            }
           
            //2. 创建房间，将用户信息添加到房间中
            std::unique_lock<std::mutex> lock(_mutex);
            //uint64_t room_id, users_tables *tb_user, online_manager *online_user
            room_ptr rp(new room(_next_rid,_tb_user,_online_user));
            rp->add_white_user(uid1);
            rp->add_black_user(uid2);

            
           
            //3. 将房间信息管理起来
            _rooms.insert(std::make_pair(_next_rid,rp));
            _users.insert(std::make_pair(uid1,_next_rid));
            _users.insert(std::make_pair(uid2,_next_rid));
            _next_rid++;
            
            //4. 返回房间信息
            return rp;
            
        }
        /*通过房间ID获取房间信息*/
        room_ptr get_room_by_rid(uint64_t rid) {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it= _rooms.find(rid);
            if(it==_rooms.end())
            {
                return room_ptr();
            }
            return it->second;


        }
        /*通过用户ID获取房间信息*/
        room_ptr get_room_by_uid(uint64_t uid) {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it=_users.find(uid);
            if(it==_users.end())
            {
                return room_ptr();
            }
            uint64_t rid= it->second;
            auto it2= _rooms.find(rid);
            if(it2==_rooms.end())
            {
                return room_ptr();
            }
            return it2->second;
           
        }
        /*通过房间ID销毁房间*/
        void remove_room(uint64_t rid) {
            //因为房间信息，是通过shared_ptr在_rooms中进行管理，因此只要将shared_ptr从_rooms中移除
            //则shared_ptr计数器==0，外界没有对房间信息进行操作保存的情况下就会释放
            //1. 通过房间ID，获取房间信息
            DBG_Log("进入房间销毁函数成功%ld",rid);
            room_ptr rp=get_room_by_rid(rid);
            if(rp.get()==nullptr)
            {
                ERR_Log("获取rp错误");
            }
           
            //2. 通过房间信息，获取房间中所有用户的ID
            
            uint64_t w_uid1=rp->get_white_user();
            uint64_t b_uid2=rp->get_black_user();

            DBG_Log("uid1%ld,uid2%ld",w_uid1,b_uid2);
            //3. 移除房间管理中的用户信息
            std::unique_lock<std::mutex> lock(_mutex);
            DBG_Log("1");
            _users.erase(w_uid1);
            _users.erase(b_uid2);
            DBG_Log("1");
            //4. 移除房间管理信息
             _rooms.erase(rid);
            DBG_Log("1");
             DBG_Log("房间销毁成功");
          
             return ;
           
        }
        /*删除房间中指定用户，如果房间中没有用户了，则销毁房间，用户连接断开时被调用*/
        void remove_room_user(uint64_t uid) {
            room_ptr rp= get_room_by_uid(uid);

            rp->handle_exit(uid);
            
            if (rp->player_count() == 0) {
                DBG_Log("移除uid:%ld成功",uid);
                remove_room(rp->id());
            }
            DBG_Log("移除uid:%ld成功",uid);
            return ;

           
           
        }
};
#endif